#include "Camera.h"

#include <GL/glut.h>
#include <NxMath.h>

Camera::Camera() {

}

Camera::Camera(const NxVec3& position, const NxVec3& direction, float aspectRatio) : upNormal(0, 1, 0) {
    this->position = position;
    this->direction = direction;
    this->direction.normalize();

    //default values
    this->fovyAngle = 60.0f;
    this->aspectRatio = aspectRatio;
    this->zNear = 0.1f;
    this->zFar = 10000.0f;
}

Camera::Camera(const NxVec3& position, const NxVec3& direction, float aspectRatio, float fovyAngle, float zNear, float zFar) : upNormal(0, 1, 0) {
    this->position = position;
    this->direction = direction;
    this->direction.normalize();
    this->aspectRatio = aspectRatio;
    this->fovyAngle = fovyAngle;	
    this->zNear = zNear;
    this->zFar = zFar;
}

Camera::~Camera() {

}

//Setters

void Camera::SetPosition(const NxVec3& pos) {
    this->position = pos;
}

void Camera::SetDirection(const NxVec3& direction) {
    this->direction = direction;
    this->direction.normalize();
}

void Camera::SetFovyAngle(float fovyAngle) {
    this->fovyAngle = fovyAngle;
}

void Camera::SetAspectRatio(float aspectRatio) {
    this->aspectRatio = aspectRatio;
}

void Camera::SetzNear(float zNear) {
    this->zNear = zNear;
}

void Camera::SetzFar(float zFar) {
    this->zFar = zFar;
}

//Getters

const NxVec3& Camera::GetPosition() const {
    return this->position;
}

const NxVec3& Camera::GetDirection() const {
    return this->direction;
}

NxVec3 Camera::GetSideNormal() const {
    return direction.cross(GetUpNormal());
}

float Camera::GetFovyAngle() const {
    return this->fovyAngle;
}

float Camera::GetAspectRatio() const {
    return this->aspectRatio;
}

float Camera::GetzNear() const {
    return this->zNear;
}

float Camera::GetzFar() const {
    return this->zFar;
}

float Camera::GetXAngle() const {
    return atan2(this->direction.y, this->direction.z);
}

float Camera::GetYAngle() const {
    return atan2(this->direction.x, this->direction.z);
}

float Camera::GetZAngle() const {
    return atan2(this->direction.y, this->direction.x);
}

//Operations

void Camera::MoveBy(const NxVec3& d) {
    this->position += d;
}

void Camera::Rotate(float xAngle, float yAngle) {
    const float xa = NxPiF32 * -xAngle / 180.0f;
    const float ya = NxPiF32 * -yAngle / 180.0f;

    NxQuat qx(xa, NxVec3(0,1,0));
    qx.rotate(this->direction);

    NxQuat qy(ya, GetSideNormal());
    qy.rotate(this->direction);
}

void Camera::Update() {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(this->fovyAngle, this->aspectRatio, this->zNear, this->zFar);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(this->position.x, this->position.y, this->position.z, this->position.x + this->direction.x, this->position.y + this->direction.y, this->position.z + this->direction.z, 0.0f, 1.0f, 0.0f);
}
